﻿/**********************************************************************
*创建人：蔡水明
*创建时间：2018/3/9 11:03:16
*说明：<Function>
/**********************************************************************/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Utilities
{
    /// <summary>
    /// Image图片处理类
    /// </summary>
    public class ImgHelper
    {
        #region 属性
        private int maxWidth = 0;
        private int maxHeight = 0;
        /// <summary>
        /// 最大宽度 0 为不限制
        /// </summary>
        public int MaxWidth
        {
            get
            {
                return maxWidth;
            }

            set
            {
                maxWidth = value;
            }
        }
        /// <summary>
        /// 最大高度 0 为不限制
        /// </summary>
        public int MaxHeight
        {
            get
            {
                return maxHeight;
            }

            set
            {
                maxHeight = value;

            }
        }
        #endregion

        /// <summary>
        /// 以非独占的方式从文件中导入图片
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public static Image LoadImage(string filename)
        {
            if (string.IsNullOrEmpty(filename))
            {
                return null;
            }
            if (!File.Exists(filename))
            {
                return null;
            }
            Image fileImage = null;
            //LOG.InfoFormat("Loading image from file {0}", filename);
            // Fixed lock problem Bug #3431881
            using (Stream imageFileStream = File.OpenRead(filename))
            {
                // And fixed problem that the bitmap stream is disposed... by Cloning the image
                // This also ensures the bitmap is correctly created

                if (filename.EndsWith(".ico"))
                {
                    // Icon logic, try to get the Vista icon, else the biggest possible
                    try
                    {
                        using (Image tmpImage = ExtractVistaIcon(imageFileStream))
                        {
                            if (tmpImage != null)
                            {
                                fileImage = Clone(tmpImage);
                            }
                        }
                    }
                    catch (Exception vistaIconException)
                    {
                        LogHelper.Warn("Can't read icon from " + filename, vistaIconException);
                    }
                    if (fileImage == null)
                    {
                        try
                        {
                            // No vista icon, try normal icon
                            imageFileStream.Position = 0;
                            // We create a copy of the bitmap, so everything else can be disposed
                            using (Icon tmpIcon = new Icon(imageFileStream, new Size(1024, 1024)))
                            {
                                using (Image tmpImage = tmpIcon.ToBitmap())
                                {
                                    fileImage = Clone(tmpImage);
                                }
                            }
                        }
                        catch (Exception iconException)
                        {
                            LogHelper.Warn("Can't read icon from " + filename, iconException);
                        }
                    }
                }
                if (fileImage == null)
                {
                    // We create a copy of the bitmap, so everything else can be disposed
                    imageFileStream.Position = 0;
                    using (Image tmpImage = Image.FromStream(imageFileStream, true, true))
                    {
                        //LOG.DebugFormat("Loaded {0} with Size {1}x{2} and PixelFormat {3}", filename, tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
                        fileImage = Clone(tmpImage);
                    }
                }
            }
            return fileImage;
        }

        public static string[] OpenImageFileDialog(bool multiselect)
        {
            using (OpenFileDialog ofd = new OpenFileDialog())
            {
                ofd.Filter = "Image files (*.png, *.jpg, *.jpeg, *.jpe, *.jfif, *.gif, *.bmp, *.tif, *.tiff)|*.png;*.jpg;*.jpeg;*.jpe;*.jfif;*.gif;*.bmp;*.tif;*.tiff|" +
                    "PNG (*.png)|*.png|JPEG (*.jpg, *.jpeg, *.jpe, *.jfif)|*.jpg;*.jpeg;*.jpe;*.jfif|GIF (*.gif)|*.gif|BMP (*.bmp)|*.bmp|TIFF (*.tif, *.tiff)|*.tif;*.tiff";

                ofd.Multiselect = multiselect;

                if (ofd.ShowDialog() == DialogResult.OK)
                {
                    return ofd.FileNames;
                }
            }

            return null;
        }
        /// <summary>
        /// 为图片生成缩略图  
        /// </summary>
        /// <param name="phyPath">原图片的路径</param>
        /// <param name="width">缩略图宽</param>
        /// <param name="height">缩略图高</param>
        /// <returns></returns>
        public static Image GetThumbnail(System.Drawing.Image image, int width, int height)
        {
            Bitmap bmp = new Bitmap(width, height);
            //从Bitmap创建一个System.Drawing.Graphics
            System.Drawing.Graphics gr = System.Drawing.Graphics.FromImage(bmp);
            //设置 
            gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            //下面这个也设成高质量
            gr.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            //下面这个设成High
            gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
            //把原始图像绘制成上面所设置宽高的缩小图
            System.Drawing.Rectangle rectDestination = new System.Drawing.Rectangle(0, 0, width, height);

            gr.DrawImage(image, rectDestination, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel);
            return bmp;
        }
        public static Bitmap ImageResizeByAforge(Bitmap mg, Size newSize)
        {
            AForge.Imaging.Filters.ResizeNearestNeighbor rs = new AForge.Imaging.Filters.ResizeNearestNeighbor(newSize.Width, newSize.Height);
            return rs.Apply(mg);
        }
        public static Bitmap GetImageThumb(Bitmap mg, Size newSize)
        {
            double ratio = 0d;
            double myThumbWidth = 0d;
            double myThumbHeight = 0d;
            int x = 0;
            int y = 0;
            Bitmap bp;

            if ((mg.Width / Convert.ToDouble(newSize.Width)) > (mg.Height /
            Convert.ToDouble(newSize.Height)))
                ratio = Convert.ToDouble(mg.Width) / Convert.ToDouble(newSize.Width);
            else
                ratio = Convert.ToDouble(mg.Height) / Convert.ToDouble(newSize.Height);
            myThumbHeight = Math.Ceiling(mg.Height / ratio);
            myThumbWidth = Math.Ceiling(mg.Width / ratio);

            Size thumbSize = new Size((int)newSize.Width, (int)newSize.Height);
            bp = new Bitmap(newSize.Width, newSize.Height);
            x = (newSize.Width - thumbSize.Width) / 2;
            y = (newSize.Height - thumbSize.Height);
            System.Drawing.Graphics g = Graphics.FromImage(bp);
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.PixelOffsetMode = PixelOffsetMode.HighQuality;
            Rectangle rect = new Rectangle(x, y, thumbSize.Width, thumbSize.Height);
            g.DrawImage(mg, rect, 0, 0, mg.Width, mg.Height, GraphicsUnit.Pixel);

            return bp;

        }
        #region 辅助方法
        /// <summary>
        /// Wrapper for just cloning which calls the CloneArea
        /// </summary>
        /// <param name="sourceImage">Image to clone</param>
        /// <returns>Bitmap with clone image data</returns>
        public static Image Clone(Image sourceImage)
        {
            if (sourceImage is Metafile)
            {
                return (Image)sourceImage.Clone();
            }
            return CloneArea(sourceImage, Rectangle.Empty, PixelFormat.DontCare);
        }
        /// <summary>
        /// Clone an image, taking some rules into account:
        /// 1) When sourceRect is the whole bitmap there is a GDI+ bug in Clone
        ///		Clone will than return the same PixelFormat as the source
        ///		a quick workaround is using new Bitmap which uses a default of Format32bppArgb
        ///	2) When going from a transparent to a non transparent bitmap, we draw the background white!
        /// </summary>
        /// <param name="sourceImage">Source bitmap to clone</param>
        /// <param name="sourceRect">Rectangle to copy from the source, use Rectangle.Empty for all</param>
        /// <param name="targetFormat">Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported)</param>
        /// <returns></returns>
        private static Bitmap CloneArea(Image sourceImage, Rectangle sourceRect, PixelFormat targetFormat)
        {
            Bitmap newImage = null;
            Rectangle bitmapRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);

            // Make sure the source is not Rectangle.Empty
            if (Rectangle.Empty.Equals(sourceRect))
            {
                sourceRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
            }
            else
            {
                sourceRect.Intersect(bitmapRect);
            }

            // If no pixelformat is supplied
            if (PixelFormat.DontCare == targetFormat || PixelFormat.Undefined == targetFormat)
            {
                if (SupportsPixelFormat(sourceImage.PixelFormat))
                {
                    targetFormat = sourceImage.PixelFormat;
                }
                else if (Image.IsAlphaPixelFormat(sourceImage.PixelFormat))
                {
                    targetFormat = PixelFormat.Format32bppArgb;
                }
                else
                {
                    targetFormat = PixelFormat.Format24bppRgb;
                }
            }

            // check the target format
            if (!SupportsPixelFormat(targetFormat))
            {
                if (Image.IsAlphaPixelFormat(targetFormat))
                {
                    targetFormat = PixelFormat.Format32bppArgb;
                }
                else
                {
                    targetFormat = PixelFormat.Format24bppRgb;
                }
            }

            bool destinationIsTransparent = Image.IsAlphaPixelFormat(targetFormat);
            bool sourceIsTransparent = Image.IsAlphaPixelFormat(sourceImage.PixelFormat);
            bool fromTransparentToNon = !destinationIsTransparent && sourceIsTransparent;
            bool isBitmap = sourceImage is Bitmap;
            bool isAreaEqual = sourceRect.Equals(bitmapRect);
            if (isAreaEqual || fromTransparentToNon || !isBitmap)
            {
                // Rule 1: if the areas are equal, always copy ourselves
                newImage = new Bitmap(bitmapRect.Width, bitmapRect.Height, targetFormat);
                // Make sure both images have the same resolution
                newImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);

                using (Graphics graphics = Graphics.FromImage(newImage))
                {
                    if (fromTransparentToNon)
                    {
                        // Rule 2: Make sure the background color is white
                        graphics.Clear(Color.White);
                    }
                    // decide fastest copy method
                    if (isAreaEqual)
                    {
                        graphics.DrawImageUnscaled(sourceImage, 0, 0);
                    }
                    else
                    {
                        graphics.DrawImage(sourceImage, 0, 0, sourceRect, GraphicsUnit.Pixel);
                    }
                }
            }
            else
            {
                // Let GDI+ decide how to convert, need to test what is quicker...
                newImage = (sourceImage as Bitmap).Clone(sourceRect, targetFormat);
                // Make sure both images have the same resolution
                newImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
            }
            // Clone property items (EXIF information etc)
            if (sourceImage.PropertyItems != null)
            {
                foreach (var propertyItem in sourceImage.PropertyItems)
                {
                    try
                    {
                        newImage.SetPropertyItem(propertyItem);
                    }
                    catch (Exception ex)
                    {
                        LogHelper.Warn("Problem cloning a propertyItem.", ex);
                    }
                }
            }
            return newImage;
        }

        /// <summary>
        /// Checks if we support the pixel format
        /// </summary>
        /// <param name="pixelformat">PixelFormat to check</param>
        /// <returns>bool if we support it</returns>
        private static bool SupportsPixelFormat(PixelFormat pixelformat)
        {
            return (pixelformat.Equals(PixelFormat.Format32bppArgb) ||
                    pixelformat.Equals(PixelFormat.Format32bppPArgb) ||
                    pixelformat.Equals(PixelFormat.Format32bppRgb) ||
                    pixelformat.Equals(PixelFormat.Format24bppRgb));
        }

        /// <summary>
        /// Based on: http://www.codeproject.com/KB/cs/IconExtractor.aspx
        /// And a hint from: http://www.codeproject.com/KB/cs/IconLib.aspx
        /// </summary>
        /// <param name="iconStream">Stream with the icon information</param>
        /// <returns>Bitmap with the Vista Icon (256x256)</returns>
        private static Bitmap ExtractVistaIcon(Stream iconStream)
        {
            const int SizeICONDIR = 6;
            const int SizeICONDIRENTRY = 16;
            Bitmap bmpPngExtracted = null;
            try
            {
                byte[] srcBuf = new byte[iconStream.Length];
                iconStream.Read(srcBuf, 0, (int)iconStream.Length);
                int iCount = BitConverter.ToInt16(srcBuf, 4);
                for (int iIndex = 0; iIndex < iCount; iIndex++)
                {
                    int iWidth = srcBuf[SizeICONDIR + SizeICONDIRENTRY * iIndex];
                    int iHeight = srcBuf[SizeICONDIR + SizeICONDIRENTRY * iIndex + 1];
                    int iBitCount = BitConverter.ToInt16(srcBuf, SizeICONDIR + SizeICONDIRENTRY * iIndex + 6);
                    if (iWidth == 0 && iHeight == 0)
                    {
                        int iImageSize = BitConverter.ToInt32(srcBuf, SizeICONDIR + SizeICONDIRENTRY * iIndex + 8);
                        int iImageOffset = BitConverter.ToInt32(srcBuf, SizeICONDIR + SizeICONDIRENTRY * iIndex + 12);
                        using (MemoryStream destStream = new MemoryStream())
                        {
                            destStream.Write(srcBuf, iImageOffset, iImageSize);
                            destStream.Seek(0, SeekOrigin.Begin);
                            bmpPngExtracted = new Bitmap(destStream); // This is PNG! :)
                        }
                        break;
                    }
                }
            }
            catch
            {
                return null;
            }
            return bmpPngExtracted;
        }
        #endregion
    }
}
